import {Group} from "./Group";
import {Entity} from "./Entity";
import {IComponent} from "./interfaces";
import {ConfigDataEntity} from "../../generate/entitas";

export abstract class AbstractEntityIndex{
	comp
	group : Group<Entity>
	index : Map<any,any>
	fields : string[]
	_onEntityAddCall
	_onEntityRemoveCall
	constructor(comp_type, group, ...fields) {
		this.comp = comp_type
		this.group = group
		this.index = new Map<string, Entity>()
		this.fields = fields
		this._onEntityAddCall = (group, entity:Entity, index:number, component:IComponent)=>{
			this.onEntityAdded(group, entity, index, component)
		}
		this._onEntityRemoveCall = (group, entity:Entity, index:number, component:IComponent)=>{
			this.onEntityRemoved(group, entity, index, component)
		}
		this.activate()
	}

	activate(){
		this.group.onEntityAdded.add(this._onEntityAddCall)
		this.group.onEntityRemoved.add(this._onEntityRemoveCall)
		this.indexEntities()
		return this
	}


	deactivate(){
		this.group.onEntityAdded.remove(this._onEntityAddCall)
		this.group.onEntityRemoved.remove(this._onEntityRemoveCall)
		this.index.clear()
	}


	indexEntities(){
		this.group.getEntities().forEach((v, k)=>{
			for (let i = 0; i < this.fields.length; i++) {
				this.addEntity(v[this.fields[i]], v)
			}
		})

	}

	abstract addEntity(key, entity)
	abstract removeEntity(key, entity)


	onEntityAdded(group, entity:Entity, index:number, component:IComponent) : void{
		for (let field1 of this.fields) {
			this.addEntity(component[field1], entity)

		}
	}
	onEntityRemoved(group, entity:Entity, index:number, component:IComponent){
		for (let field1 of this.fields) {
			this.removeEntity(component[field1], entity)
		}
	}
}

export class EntityIndex extends AbstractEntityIndex{
	getEntities(key) : Array<Entity>{
		if (!this.index.has(key)){
			this.index.set(key, [])
		}
		return this.index.get(key)
	}

	addEntity(key, entity) {
		this.getEntities(key).push(entity)
	}

	removeEntity(key, entity) {
		this.getEntities(key).remove(entity)
	}

}

export class PrimaryEntityIndex extends AbstractEntityIndex{
	getEntity(key) : Entity{
		return this.index.get(key)
	}
	addEntity(key, entity) {
		if (this.index.has(key)){
			console.log(`"Entity for key '${key}' already exists!, Only one entity for a primary key is allowed.`)
			process.exit(1)
		}
		this.index.set(key, entity)
	}

	removeEntity(key, entity) {
		this.index.delete(key)
	}
}

export class ComplexCompData{
	public compType : string
	public field : string
}

export class ComplexEntityIndex{
	group : Group<Entity>
	index : Map<any,any>
	fieldDates : ComplexCompData[]
	_onEntityAddCall
	_onEntityRemoveCall

	constructor(group, ...complexCompDataArray : ComplexCompData[]) {
		this.group = group
		this.index = new Map<string, Entity>()
		this.fieldDates = complexCompDataArray
		this._onEntityAddCall = (group, entity:Entity, index:number, component:IComponent)=>{
			this.onEntityAdded(group, entity, index, component)
		}
		this._onEntityRemoveCall = (group, entity:Entity, index:number, component:IComponent)=>{
			this.onEntityRemoved(group, entity, index, component)
		}
		this.activate()
	}

	getEntities(...keys) : ConfigDataEntity[]{
		let map = this.index
		for (let i = 0; i < keys.length - 1; i++) {
			if (!map.has(keys[i])){
				return []
			}else{
				map = map.getOrDefault(keys[i], new Map<any, any>())
			}
		}
		let retList : Array<any> = map.getOrDefault(keys[keys.length - 1], [])
		return retList
	}

	activate(){
		this.group.onEntityAdded.add(this._onEntityAddCall)
		this.group.onEntityRemoved.add(this._onEntityRemoveCall)
		this.indexEntities()
		return this
	}


	deactivate(){
		this.group.onEntityAdded.remove(this._onEntityAddCall)
		this.group.onEntityRemoved.remove(this._onEntityRemoveCall)
		this.index.clear()
	}


	indexEntities(){
		this.group.getEntities().forEach((v, k)=>{
			this.addEntity( v)
		})

	}

	addEntity(entity : Entity){
		let map = this.index
		for (let i = 0; i < this.fieldDates.length - 1; i++) {
			let data = this.fieldDates[i]
			var compName = data.compType
			var comp = entity.getComponent(compName)
			map = map.getOrDefault(comp[data.field], new Map<any, any>())
		}
		let data = this.fieldDates[this.fieldDates.length - 1]
		var comp = entity.getComponent(data.compType)
		let retList : Array<any> = map.getOrDefault(comp[data.field], [])
		retList.push(entity)
	}

	removeEntity(entity, component : IComponent){
		let map = this.index
		for (let i = 0; i < this.fieldDates.length - 1; i++) {
			let data = this.fieldDates[i]
			var compName = data.compType
			let comp
			if (component.__compName == compName){
				comp = component
			}else{
				comp = entity.getComponent(compName)
			}
			map = map.getOrDefault(comp[data.field], new Map<any, any>())
		}
		let data = this.fieldDates[this.fieldDates.length - 1]
		let comp
		if (component.__compName == data.compType){
			comp = component
		}else{
			comp = entity.getComponent(data.compType)
		}
		let retList : Array<any> = map.getOrDefault(comp[data.field], [])
		retList.remove(entity)
	}


	onEntityAdded(group, entity:Entity, index:number, component:IComponent) : void{
		this.addEntity(entity)
	}
	onEntityRemoved(group, entity:Entity, index:number, component:IComponent){
		this.removeEntity(entity, component)

	}

}